/*
 * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
 *
 * This software is distributed under the terms of the 3-clause BSD license.
 * See file LICENSE for a full version of the license.
 */

#pragma once

#include <corsika/framework/process/SecondariesProcess.hpp>
#include <corsika/framework/process/CascadeEquationsProcess.hpp>

#include <corsika/framework/core/PhysicalUnits.hpp>
#include <corsika/framework/core/ParticleProperties.hpp>
#include <corsika/framework/geometry/Point.hpp>
#include <corsika/framework/geometry/Vector.hpp>
#include <corsika/media/ShowerAxis.hpp>

#include <corsika/modules/writers/SubWriter.hpp>
#include <corsika/modules/writers/EnergyLossWriter.hpp>
#include <corsika/modules/writers/LongitudinalWriter.hpp>

#include <corsika/modules/conex/CONEX_f.hpp>

/**
 * @file CONEXhybrid.hpp
 */

namespace corsika {

  namespace conex {
    LengthType constexpr earthRadius{6371315 * meter};
  } // namespace conex

  /**
   * Access to the CONEX model.
   *
   * The fortran version of CONEX is interfaced. This is a SecondariesProcess since it
   * skims the Stack for input particles (specific energies, or types) in order to fill
   * internal CONEX source tables. CONEX is the called after the Cascade is finished to do
   * a CascadeEquations step. This typically produces output data, and in addition can
   * even generate new secondaries on the main Stack.
   *
   * Note that the output is processed by `SubWriter<TOutputE/N>`. The SubWriters have to
   * be initialized with valid objects of type TOutputE/N at construction time.If no
   * output is wished, `WriterOff` can be used for this purpose.
   *
   * @tparam TOutputE -- Output writer for dEdX data.
   * @tparam TOutputN -- Output writer for particle number profile data.
   */
  template <typename TOutputE, typename TOutputN>
  class CONEXhybrid : public CascadeEquationsProcess<CONEXhybrid<TOutputE, TOutputN>>,
                      public SecondariesProcess<CONEXhybrid<TOutputE, TOutputN>>,
                      public SubWriter<TOutputE>,
                      public SubWriter<TOutputN> {

  public:
    /**
     * Constructor with output sink specified.
     *
     * The output will be generated by the output sink.
     *
     * @param center center of earth.
     * @param showerAxis shower axis to convert geometry to grammage.
     * @param groundDist distance to ground.
     * @param injectionHeight height of injection.
     * @param primaryEnergy energy of primary particle
     * @param pdg type of primary particle.
     * @param outputE object to initialized SubWriter<TOutputE>
     * @param outputN object to initialized SubWriter<TOutputN>
     */
    CONEXhybrid(Point const& center, ShowerAxis const& showerAxis, LengthType groundDist,
                LengthType injectionHeight, HEPEnergyType primaryEnergy, PDGCode pdg,
                TOutputE& outputE, TOutputN& outputN);

    template <typename TStackView>
    void doSecondaries(TStackView&);

    /**
     * init currently needs to be called to initializa a new
     * event. All tables are cleared, etc.
     */
    void initCascadeEquations();

    /**
     * Cascade equations are solved basoned on the data in the tables.
     */
    template <typename TStack>
    void doCascadeEquations(TStack& stack);

    /**
     * Internal function to fill particle data inside CONEX
     * tables. Only e.m. particles are selected right now.
     */
    bool addParticle(Code pid, HEPEnergyType energy, HEPEnergyType mass,
                     Point const& position, Vector<dimensionless_d> const& direction,
                     TimeType t, double weight = 1);

    CoordinateSystemPtr const& getObserverCS() const { return conexObservationCS_; }

    YAML::Node getConfig() const final override;

  private:
    // data members
    //! CONEX e.m. particle codes
    static std::array<std::pair<Code, int>, 3> constexpr egs_em_codes_{
        {{Code::Photon, 0}, {Code::Electron, -1}, {Code::Positron, -1}}};

    Point const center_; //!< center of CONEX Earth
    ShowerAxis const& showerAxis_;
    LengthType groundDist_;       //!< length from injection point to shower core
    LengthType injectionHeight_;  //!< starting height of primary particle
    HEPEnergyType primaryEnergy_; //!< primary particle energy
    PDGCode primaryPDG_;          //!< primary particle PDG
    Point const showerCore_;      //!< shower core
    CoordinateSystemPtr const conexObservationCS_; //!< CONEX observation frame
    DirectionVector const x_sf_,
        y_sf_; //!< unit vectors of CONEX shower frame, z_sf is shower axis direction
  };
} // namespace corsika

#include <corsika/detail/modules/conex/CONEXhybrid.inl>
